import {extract,intervalExtract,varTypeof} from "./signalFunc.js"
/**
 *@description 缓存时域数据
 *@param {Number} cacheTimeLen 缓存时长 默认为1s
 *	若为数字，所有通道用同一缓存时长
 *	若为对象，可分别指定不同通道的缓存时长 如{"1":1,"2":2,"3":3}
 *@param {Number} channelNum 缓存通道数 默认为4 缓存数据用通道序号标识 
 *@param {boolean} isMultiData 通道数据是否为多曲线 默认为false 缓存数据用通道序号标识 
 *  若为boolean，所有通道都是一组数据或多组数据
 *	若为对象，可分别指定不通道的数据形式 如{"1":true,"2":true,"3":false}
 *@param {Number/Object} fs采样频率
 *	若为数字，所有通道用同一采样频率
 *	若为对象，可分别指定不同通道的采集频率 如{"1":1000,"2":10000,"3":20000}
 */
function TDCache(fs,cacheTimeLen=1,channelNum=4,isMultiData=false){
	let cache={};
	//初始化通道频率、数据形式
	let fsObj={},multiData={},cacheTimeLenObj={};
	let isNum=varTypeof(fs)=='number';
	let isNum2=varTypeof(isMultiData)=='boolean';
	let isNum3=varTypeof(cacheTimeLen)=='number';
	for(let i=1;i<=channelNum;i++){
		fsObj[`O${i}`]=isNum?fs:fs[i];
		multiData[`O${i}`]=isNum2?isMultiData:isMultiData[i];
		cacheTimeLenObj[`O${i}`]=isNum3?cacheTimeLen:cacheTimeLen[i];
	}	
	//初始化通道数据
	for(let i=1;i<=channelNum;i++){
		let key=`O${i}`;
		cache[key]={
			//通道索引
			channel:i,
			//采集频率
			fs:fsObj[key],
			//是否为多组数据
			isMultiData:multiData[key],
			//最大缓存点数  采集频率*缓存时间
			maxLen:fsObj[key]*cacheTimeLenObj[key],
			//缓存数据
			value:[],
			//数据对应横坐标
			xAxis:[],
			//数据对应横坐标
			insert(data,xAxis){
				insert(this.channel,data,xAxis);
			},
			//获取数据 start end 分别为开始与结束位置的百分比
			getData(start,end){
				return getData(this.channel,start,end)
			},
			//清空数据
			clear(){
				let key2=`O${i}`;
				cache[key2].value=[];
				cache[key2].xAxis=[];
				cache[key2].newData=false;
			},
			//获取精简数据 start end 分别为开始与结束位置的百分比 len为返回的数据长度
			extract(start,end,len=2000){
				let data=getData(this.channel,start,end);
				if(this.isMultiData){
					return intervalExtract(data.xAxis,data.value,len);
				}else{				
					return extract(data.xAxis,data.value,len);
				}
			}
		}
	}
	//清空所有通道缓存数据
	function clear(){
		for(let i in cache){
			cache[i].value=new Array(maxLen).fill("");
			cache[i].xAxis=new Array(maxLen).fill("");
		}
	}
	/**
	 * 添加数据
	 * @param {Number} channel 数据所属通道
	 * @param {Array} data 通道数据
	 * @param {Array} xAxis 数据对应横坐标
	 * @param {boolean} isMultiData 数据是否为多组数据
	 */
	function insert(channel,data,xAxis){
		let channelData=cache[`O${channel}`];
		if(!channelData.isMultiData){
			//单组数据时
			let len=data.length;
			if(channelData.value.length==0){
				channelData.value=new Array(channelData.maxLen).fill("");
				channelData.xAxis=new Array(channelData.maxLen).fill("");
			}
			for(let i=0;i<len;i++){
				channelData.value.push(data[i]);
				channelData.xAxis.push(xAxis[i]);
			}
			//数据量超过最大长度时
			if(channelData.value.length>channelData.maxLen){
				channelData.value.splice(0,data.length);
				channelData.xAxis.splice(0,data.length);
			}
		}else{
			//多组数据时
			let len=data[0].length;
			let len2=data.length;
			for(let i=0;i<len;i++){
				for(let k=0;k<len2;k++){
					//若该组数据不是数组类型，则表示未初始化
					if(varTypeof(channelData.value[k])!='array'){
						channelData.value[k]=new Array(channelData.maxLen).fill("")
					}
					channelData.value[k].push(data[k][i]);
				}
				channelData.xAxis.push(xAxis[i]);
			}
			if(channelData.value[0].length>channelData.maxLen){
				for(let k=0;k<len2;k++){
					channelData.value[k].splice(0,data[k].length);
				}
				channelData.xAxis.splice(0,xAxis.length);
			}
		}		
		channelData.newData=true;
	}
	
	/**
	 * 获取片段数据
	 * @param {Number} start 开始位置百分比
	 * @param {Number} end 结束位置百分比
	 * @return {Object} 通道数据
	 */
	function getData(channel,start=0,end=1){
		let channelData=cache[`O${channel}`],value;
		start*=channelData.maxLen;
		end*=channelData.maxLen;
		if(channelData.isMultiData){
			//多组数据时
			value=[];
			channelData.value.forEach(v=>{
				value.push(v.slice(start,end));
			})
		}else{
			//单组数据时
			value=channelData.value.slice(start,end);
		}
		return {
			value,
			xAxis:channelData.xAxis.slice(start,end)
		}
	}
	/**
	 * 获取通道对象
	 * @param {Number} channel 通道索引号
	 * @return {Object} 返回通道对象
	 */
	function getChannel(channel){
		return cache[`O${channel}`]
	}
	return {
		getChannel,
		clear
	}
}

/**
 * @description 缓存处理结果
 */
function resultCache(){
	/**
	 * @description 创建缓存对象
	 * @param {Number} cacheMaxLen 最大缓存数据条数 默认为1,大于1时将以数组形式缓存 
	 * @returns {Object} {add,clear,get} 缓存操作对象
	 */
	function createCache(cacheMaxLen=1){
		let cache=null;
		if(varTypeof(cacheMaxLen)!='number' || cacheMaxLen<1){
			cacheMaxLen=1;
		}
		/**
		 * @description 在当前缓存对象添加缓存数据,saveAll为true时，将以数组形式记录所有数据
		 * @param data 需要缓存的纵坐标数据
		 * @param xAxis 需要缓存横坐标数据
		 */
		this.insert=function(value,xAxis){
			if(cacheMaxLen>1){
				cache.push({value,xAxis});
				if(cache.length>cacheMaxLen){
					cache.splice(0,1);
				}
			}else{
				cache={value,xAxis};
			}
			if(varTypeof(value[0])=='array'){
				this.maxLen=value[0].length;
			}else{
				this.maxLen=value.length;
			}
		}
		/**
		 * @description 清除所有缓存数据
		*/
		this.clear=function(){
			cache=null
		}
		/**
		 * @deprecated 提取缓存数据
		 * @param {Array/Number} range 提取参数
		 *   cacheMaxLen==1时,将忽略此参数，返回所有缓存
		 *   cacheMaxLen>1时,数据将以数组的形式缓存
		 * 		为空返回最后一条缓存
		 * 		为数字返回指定索引号缓存
		 * 		为数组时返回数组范围的缓存,如[1,10]将返回索引号为1-9的缓存
		 * @returns 返回缓存数据
		 */
		this.getData=function(range=null){
			if(cacheMaxLen==1){
				return cache;
			}else if(range==null){
				return cache[cache.length-1];
			}else{
				let type=varTypeof(range);
				if(type=='number'){
					return cache[range];
				}
				if(type=='array'){
					let min=Math.min(...range);
					let max=Math.max(...range);
					cache.slice(min,max);
				}
				return null;
			}					
		}
		
		//获取精简数据 start end 分别为开始与结束位置的百分比 len为返回的数据长度
		this.extract=function(start,end,len=2000){
			start*=this.maxLen;
			end*=this.maxLen;
			let data=cacheMaxLen==1?cache:cache[cache.length-1],cacheData;
			
			if(varTypeof(data.value)=='array'){
				cacheData={
					value:[],
					xAxis:data.xAxis.slice(start,end),
				}
				data.value.forEach(=>{
					cacheData.value.push(v.slice(start,end))
				});
				return intervalExtract(data.xAxis,data.value,len);
			}else{
				cacheData={
					value:data.data.slice(start,end),
					xAxis:data.xAxis.slice(start,end)
				}
				return extract(cacheData.xAxis,cacheData.value,len);
			}
		}
	}
	
	/**
	 * @description 创建缓存列表管理对象
	 */
	function createCacheList(){
		this.cacheList={};//缓存列表，缓存以key:value形式存储
		
		/**
		 * @param {String} cacheName 缓存名称
		 * @param {Object} cacheMaxLen
		 * @param {Object} 返回createCache 对象
		 */
		this.add=function(cacheName,cacheMaxLen){
			if(['add','clear'].indexOf(cacheName)>-1){
				return null;
			}
			if(this.cacheList[cacheName]){
				this.cacheList[cacheName]=new createCache(cacheMaxLen);
			}
			return this.cacheList[cacheName];
		}
		/**
		 * 清除缓存对象
		 * @param {String} cacheName 缓存名称
		 * @param {Object} cacheMaxLen
		 * @param {Object} 返回createCache 对象
		 */
		this.clear=function(cacheName){
			if(cacheName){
				if(this.cacheList[cacheName]){
					this.cacheList[cacheName].clear();
					delete this.cacheList[cacheName];
				}
				return;
			}
			for(let i in this.cacheList){
				this.cacheList[i].clear();
			}
			this.cacheList={}
		}
	}
	
	let func=new createCacheList();	
	return new Proxy({},{
		get(target,key){
			return func[key] || func.cacheList[key];
		}
	}
}

/**
 * @description echart扩展
 * @param {Object} echartObj echart对象
 * @param {Object} channelData cacheData对应的通道对象
 */
function echartExtend(echartObj,channelData){
	let maxLen=channelData.maxLen-1;
	let obj=echartObj.getDom().querySelector('canvas');
	let step=0.1,left,right,channel;
	echartObj.channel=channelData;
	echartObj.startPos=0;
	echartObj.endPos=maxLen;
	//获取左右间隙
	reloadGap();
	//鼠标滚轮
	obj.onmousewheel=ev=>{
		ev.preventDefault();
		let bl=(ev.clientX-left)/(obj.offsetWidth-right-left);
		let changeLen=(echartObj.endPos-echartObj.startPos)*step;
		let leftNum=bl*changeLen;
		let rightNum=(1-bl)*changeLen;
		if(ev.deltaY>0){
			//缩小
			if(echartObj.startPos==0 && echartObj.endPos==maxLen)return;
			echartObj.startPos-=leftNum;
			echartObj.endPos+=rightNum;
			if(echartObj.startPos<0)echartObj.startPos=0;
			if(echartObj.endPos>maxLen)echartObj.endPos=maxLen;
		}else{
			//放大
			if(echartObj.endPos-echartObj.startPos<50)return;
			echartObj.startPos+=leftNum;
			echartObj.endPos-=rightNum;
		}
		let start=echartObj.startPos/maxLen;
		let end=echartObj.endPos/maxLen;
		draw(start,end);
	}
	//鼠标拖动
	let posX=null;
	let w=obj.offsetWidth-right-left;
	let timer,startPos2,endPos2;
	obj.onmousemove=ev=>{
		if(timer)return;
		timer=setTimeout(()=>{
			timer=null;
			if(posX!=null){
				let c=ev.offsetX-posX;
				let len=Math.abs((c/w)*(echartObj.endPos-echartObj.startPos));
				if(c>0){//左移
					if(startPos2==0)return;
					if(echartObj.startPos-len<0)len=echartObj.startPos;
					startPos2=echartObj.startPos-len;
					endPos2=echartObj.endPos-len;
				}else{//右移
					if(endPos2==maxLen)return;
					if(echartObj.endPos+len>maxLen)len=maxLen-echartObj.endPos;
					startPos2=echartObj.startPos+len;
					endPos2=echartObj.endPos+len;
				}
				let start=startPos2/maxLen;
				let end=endPos2/maxLen;
				draw(start,end);
			}
		},100);
	}
	obj.onmousedown=ev=>{
		posX=ev.offsetX;
		startPos2=echartObj.startPos;
		endPos2=echartObj.endPos;
	}
	obj.onmouseup=ev=>{
		posX=null;
		if(startPos2)echartObj.startPos=startPos2;
		if(endPos2)echartObj.endPos=endPos2;
	}
	obj.onmouseout=ev=>{
		posX=null;
		if(startPos2)echartObj.startPos=startPos2;
		if(endPos2)echartObj.endPos=endPos2;
	}
	//计算左右两边留白距离
	function reloadGap(){
		let grid=echartObj.getOption().grid[0];
		left=grid.left.indexOf('%')?obj.offsetWidth*grid.left.replace('%',"")/100:grid.left;
		right=grid.right.indexOf('%')?obj.offsetWidth*grid.right.replace('%',"")/100:grid.right;
	}
	//画图表 start end为开始位置和结束位置的百分比
	function draw(start,end){
		if(!echartObj.channel)return;
		let res=echartObj.channel.extract(start,end);
		//console.log('计算结果',res.x.slice(0,5))
		//有多组数据时
		let t=Date.now();
		if(echartObj.channel.isMultiData){
			let series=[];
			res.y.forEach(v=>series.push({data:v}));
			echartObj.setOption({
				series,
				xAxis:{data:res.x,interval:res.interval}
			});
		}else{
			echartObj.setOption({
				series:[{data:res.y}],
				xAxis:{data:res.x,interval:res.interval}
			});
		}
		let t2=Date.now()-t;
		if(t2>50)console.log('画图',t2)
		
	}
	//设置图表通道数据
	function setChannel(data){
		echartObj.channel=data;
		maxLen=echartObj.channel.getData().value.length-1;
	}
	
	echartObj.startUpdate=function(times=20){
		function a(){
			if(!echartObj.timer)return;
			setTimeout(()=>{
				if(channel && channel.newData){
					channel.newData=false;
					echartObj.reload();
				}
				a();
			},parseInt(1000/times));
		}
		echartObj.timer=false;
		setTimeout(()=>{
			echartObj.timer=true;
			a();
		},200);
	}
	echartObj.stopUpdate=function(){
		echartObj.timer=false;
	}
	echartObj.reloadGap=reloadGap;
	echartObj.setChannel=setChannel;
	echartObj.draw=draw;
	echartObj.reload=function(){
		let start=echartObj.startPos/maxLen;
		let end=echartObj.endPos/maxLen;
		draw(start,end);
	}
	return echartObj;
}

/**
 * @description 多个echart实例管理
 */
function echartManager(channelsObj){
	let objs=[];
	let timer3;
	return {
		add(echartObj,channel){
			echartExtend(echartObj,channelsObj.getChannel(channel));
			objs.push(echartObj);
		},
		del(echartObj){
			for(let i=0;i<objs.length;i++){
				if(objs[i]==echartObj){
					objs.splice(i,1);
					break;
				}
			}
		},
		startUpdate(times=20,debug=false){
			let count=0;
			let t3=Date.now(),t;
			window.dataCount=0;
			function a(){
				if(!timer3)return;
				setTimeout(()=>{
					t=Date.now();
					for(let i=0;i<objs.length;i++){
						if(objs[i].channel && objs[i].channel.newData){
							objs[i].channel.newData=false;
							objs[i].reload();
						}
					}
					if(debug){
						if(Date.now()-t>2){
							if(!window.debugDiv){
								window.debugDiv=document.createElement('div');
								window.debugDiv.style.cssText="position:fixed;top:0;left:50%;background:rgba(0,0,0,.7);color:#f2f2f2;font-size:14px;padding:5px 10px;";
								document.body.append(window.debugDiv);
							}
							window.debugDiv.innerHTML=`
								更新次数:<span style="min-width:30px;display:inline-block;padding-right:5px">${count}</span>
								帧率:<span style="min-width:30px;display:inline-block;padding-right:5px">${(count/((Date.now()-t3)/1000)).toFixed(1)}</span>
								收到数据:${window.dataCount}
							`
							count++;
						}
					}
					a();
				},1000/times);
			}
			timer3=false;
			setTimeout(()=>{
				timer3=true;
				a();
			},200);
			
		},
		stopUpdate(){
			timer3=false;
		}
	}
}

/**
 * @description worker回调处理
 */
function workerManager(){
	let worker=new Worker("./worker.js");
	let handleCallback={};
	let proxyhandle={};
	let id=0;
	worker.onmessage=ev=>{
		let msg=ev.data;
		try{
			msg.data=JSON.parse(msg.data);
		}catch(e){}
		if(msg.type=='soc'){
			handleCallback[msg.socketName](msg.data);
		}else if(proxyhandle[msg.id]){
			proxyhandle[msg.id].resolve(msg.data);
			delete proxyhandle[msg.id];
		}
		ev=null;
	}
	
	function msgCallback(socketName,key,data={}){
		id=id>2000000?1:id+1;
		worker.postMessage({
			id,
			type:key,
			socketName:socketName,
			...data
		});
		proxyhandle[id]=Promise.withResolvers();
		return proxyhandle[id].promise;
	}
	
	return {		
		//打开新连接
		async open(socketName,ws,callback){
			if(!socketName){
				console.error('websocket标识名称不存在，创建新连接失败');
				return;
			}
			handleCallback[socketName]=callback;
			let openResponse=await msgCallback(socketName,'open',{ws});
			if(openResponse){
				return new Proxy({},{
					get(target,key){
						if(key=='then')return null;
						return (data={})=>{
							return msgCallback(socketName,key,data)
						}
					}
				});
			}else{
				return null;
			}
		},
		//调用其它与websocket无关的方法
		exec:new Proxy({},{
			get(target,key){
				if(key=='then')return null;
				return (data={})=>{
					if(!data.key)Promise.resolve(null);
					id=id>2000000?1:id+1;										
					worker.postMessage({id,...data});
					proxyhandle[id]=Promise.withResolvers();
					return proxyhandle[id].promise;
				}
			}
		}),
		destroy(){
			worker.terminate();
			worker=null;
			handleCallback={};
		},
		worker
	}
}

export {
	TDCache,
	echartManager,
	workerManager,
	resultCache
}